package flow_engine

import (
	"gitee.com/whit/flow-engine/errs"
	"gitee.com/whit/flow-engine/model"
	"github.com/pkg/errors"
)

// checkProcDefConf 检查流程定义配置是否合规
// 规则如下：
// 1、必须是一个标准json字符串
// 2、根节点必须是 model.NtStart 节点, 且只能出现一次
// 3、最后一个节点必须是 model.NtEnd 节点
// 4、model.NtCondition 节点后的节点必须是多个任务节点。且相互之间的 进入条件字段必须相同，但是操作符或值不同，操作符仅支持 == != >= <= > <
func checkProcDefConf(confJson string) error {
	profDef := &model.ProcDefNode{}
	if err := json.Unmarshal([]byte(confJson), profDef); err != nil {
		// 不是一个标准的json
		return errs.NewErrInvalidProcDef(err)
	}

	if profDef.Type != model.NtStart {
		return errs.NewErrInvalidProcDef(errors.New("根节点必须是开始节点"))
	}

	return checkNode(profDef.NextNodes, profDef)
}

// checkNode 检查节点
func checkNode(nodes []model.ProcDefNode, lastNode *model.ProcDefNode) error {
	if nodes == nil || len(nodes) == 0 {
		if lastNode.Type != model.NtEnd {
			return errs.NewErrInvalidProcDef(errors.New("末端节点必须是'结束'节点"))
		}
		return nil
	}
	for _, v := range nodes {
		if v.Type == model.NtStart { // 如果是开始节点
			return errs.NewErrInvalidProcDef(errors.New("开始节点不能在根节点以外的地方出现"))
		}
		if v.Type == model.NtCondition { // 如果是条件节点
			if v.NextNodes == nil || len(v.NextNodes) == 0 {
				return errs.NewErrInvalidProcDef(errors.New("条件节点后必须跟多个节点"))
			}

			var field string
			for i, v := range v.NextNodes {
				if v.Condition == nil || v.Condition.Field == "" || v.Condition.Op == "" || v.Condition.Value == "" {
					return errs.NewErrInvalidProcDef(errors.New("条件节点后的节点必须填写进入条件"))
				}
				// 检查同级的节点，field必须相同
				if i == 0 {
					field = v.Condition.Field
				} else {
					if field != v.Condition.Field {
						return errs.NewErrInvalidProcDef(errors.New("条件节点的下一级节点之间的条件field必须相同"))
					}
				}
				// 检查操作符是否合规
				if v.Condition.Op != "==" &&
					v.Condition.Op != "!=" &&
					v.Condition.Op != ">=" &&
					v.Condition.Op != "<=" &&
					v.Condition.Op != ">" &&
					v.Condition.Op != "<" {
					return errs.NewErrInvalidProcDef(errors.New("条件比较操作符仅支持 == != >= <= > <"))
				}
			}
		}
		if v.Type == model.NtEnd {
			if lastNode.Type != model.NtTask {
				return errs.NewErrInvalidProcDef(errors.New("'结束'节点的前一个节点必须为任务节点"))
			}
			if v.NextNodes != nil && len(v.NextNodes) > 0 {
				return errs.NewErrInvalidProcDef(errors.New("'结束'节点后不允许再有节点"))
			}
			return nil
		}

		if err := checkNode(v.NextNodes, &v); err != nil {
			return err
		}
	}
	return nil
}
